package cn.jbolt.core.controller.base;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import cn.jbolt.core.api.JBoltApiRet;
import cn.jbolt.core.base.JBoltInjector;
import cn.jbolt.core.base.JBoltMsg;
import cn.jbolt.core.base.JBoltPageSize;
import cn.jbolt.core.bean.JBoltDateRange;
import cn.jbolt.core.bean.RangeSlider;
import cn.jbolt.core.bean.SortInfo;
import cn.jbolt.core.captcha.JBoltCaptchaRender;
import cn.jbolt.core.consts.JBoltConst;
import cn.jbolt.core.converter.JBoltTimestampConverter;
import cn.jbolt.core.db.sql.Sql;
import cn.jbolt.core.kit.JBoltControllerKit;
import cn.jbolt.core.model.base.JBoltBaseModel;
import cn.jbolt.core.para.IJBoltParaValidator;
import cn.jbolt.core.para.JBoltPara;
import cn.jbolt.core.para.JBoltParaValidator;
import cn.jbolt.core.poi.excel.JBoltExcel;
import cn.jbolt.core.poi.word.JBoltWord;
import cn.jbolt.core.render.JBoltByteFileType;
import cn.jbolt.core.render.JBoltByteRender;
import cn.jbolt.core.render.JBoltByteRenderType;
import cn.jbolt.core.service.base.JBoltBaseService;
import cn.jbolt.core.ui.jbolttable.JBoltTable;
import cn.jbolt.core.ui.jbolttable.JBoltTableMulti;
import cn.jbolt.core.util.JBoltArrayUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.jfinal.core.Controller;
import com.jfinal.core.JFinal;
import com.jfinal.core.NotAction;
import com.jfinal.kit.Kv;
import com.jfinal.kit.Ret;
import com.jfinal.kit.StrKit;
import com.jfinal.log.Log;
import com.jfinal.plugin.activerecord.Model;
import com.jfinal.plugin.activerecord.Record;
import com.jfinal.upload.ProgressUploadFileConfig;
import com.jfinal.upload.UploadFile;
import com.jfinal.upload.UploadProgress;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.ProgressListener;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.math.BigDecimal;
import java.util.*;
import java.util.Map.Entry;
import java.util.function.Consumer;
import java.util.regex.Pattern;

/**
 * 封装controller层简化用法 和常用方法
 *
 * @ClassName: CommonController
 * @author: JFinal学院-小木 QQ：909854136
 * @date: 2019年8月8日
 */
public abstract class JBoltCommonController extends Controller implements IJBoltParaValidator {
    protected static final Log LOG = Log.getLog("JBoltControllerLog");
    protected static final String CONFIRM_REQTYPE_GET = "GET";//renderJsonConfirm reqType GET
    protected static final String CONFIRM_REQTYPE_POST = "POST";//renderJsonConfirm reqType POST
    protected static final String CONFIRM_REQTYPE_DOWNLOAD = "DOWNLOAD";//renderJsonConfirm reqType DOWNLOAD
    protected static final String TRUE = Sql.TRUE;
    protected static final String SEPARATOR = File.separator;
    protected static final String TRUE_STR = "true";
    protected static final String FALSE = Sql.FALSE;
    protected static final String FALSE_STR = "false";
    protected static final String PAGE_PARAM_NAME = "page";
    protected static final String PAGESIZE_PARAM_NAME = "pageSize";
    protected static final String KEYWORDS_PARAM_NAME = "keywords";
    protected static final String START_TIME_PARAM_NAME = "startTime";
    protected static final String DATE_RANGE_PARAM_NAME = "dateRange";
    protected static final String DATA_RANGE_PARAM_NAME = "data";
    protected static final String END_TIME_PARAM_NAME = "endTime";
    protected static final String OFMODULE_PARAM_NAME = "ofModule";
    protected static final String ENABLE_PARAM_NAME = "enable";
    protected static final String IS_DELETED_PARAM_NAME = "is_deleted";
    protected static final String TYPE_PARAM_NAME = "type";
    protected static final String TYPE_ID_PARAM_NAME = "typeId";
    protected static final String STATE_PARAM_NAME = "state";
    protected static final String ID_PARAM_NAME = "id";
    protected static final String PID_PARAM_NAME = "pid";

    /**
     * 判断Object参数有效性
     *
     * @param param
     */
    @NotAction
    @Override
    public boolean isOk(Object param) {
        return JBoltParaValidator.isOk(param);
    }

    /**
     * 判断Object参数是否无效
     */
    @NotAction
    @Override
    public boolean notOk(Object param) {
        return JBoltParaValidator.notOk(param);
    }

    /**
     * 判断record参数有效性
     *
     * @param record
     */
    @NotAction
    @Override
    public boolean isOk(Record record) {
        return JBoltParaValidator.isOk(record);
    }

    /**
     * 判断record参数是否无效
     *
     * @param record
     */
    @NotAction
    @Override
    public boolean notOk(Record record) {
        return JBoltParaValidator.notOk(record);
    }

    /**
     * 判断model参数有效性
     *
     * @param model
     */
    @NotAction
    @Override
    public boolean isOk(Model<? extends Model<?>> model) {
        return JBoltParaValidator.isOk(model);
    }

    /**
     * 判断record参数是否无效
     *
     * @param model
     */
    @NotAction
    @Override
    public boolean notOk(Model<? extends Model<?>> model) {
        return JBoltParaValidator.notOk(model);
    }

    /**
     * 判断Object参数为Null
     *
     * @param param
     */
    @NotAction
    @Override
    public boolean isNull(Object param) {
        return JBoltParaValidator.isNull(param);
    }

    /**
     * 判断Object参数不为Null
     */
    @NotAction
    @Override
    public boolean notNull(Object param) {
        return JBoltParaValidator.notNull(param);
    }

    /**
     * 存在有效参数
     */
    @NotAction
    @Override
    public boolean hasOk(Object... params) {
        return JBoltParaValidator.hasOk(params);
    }

    /**
     * 存在无效参数
     */
    @NotAction
    @Override
    public boolean hasNotOk(Object... params) {
        return JBoltParaValidator.hasNotOk(params);
    }

    /**
     * 判断上传文件是图片
     *
     * @param contentType
     * @return
     */
    @NotAction
    @Override
    public boolean isImage(String contentType) {
        return JBoltParaValidator.isImage(contentType);
    }

    /**
     * 判断上传文件不是图片
     *
     * @param contentType
     * @return
     */
    @NotAction
    public boolean notImage(String contentType) {
        return JBoltParaValidator.notImage(contentType);
    }

    /**
     * 判断上传文件类型不是图片
     *
     * @param file
     */
    @NotAction
    @Override
    public boolean notImage(UploadFile file) {
        return JBoltParaValidator.notImage(file);
    }

    /**
     * 判断上传文件类型是否为图片
     *
     * @param file
     */
    @NotAction
    @Override
    public boolean isImage(UploadFile file) {
        return JBoltParaValidator.isImage(file);
    }

    /**
     * 判断Object[]数组类型数据是否正确
     *
     * @param param
     * @return
     */
    @NotAction
    @Override
    public boolean isOk(Object[] param) {
        return JBoltParaValidator.isOk(param);
    }

    /**
     * 判断Object[]数组类型数据不正确
     *
     * @param param
     * @return
     */
    @NotAction
    @Override
    public boolean notOk(Object[] param) {
        return JBoltParaValidator.notOk(param);
    }

    /**
     * 获取Html5中type="datetime-local"的数据 返回Date
     *
     * @param name
     * @return
     */
    protected Date getDateTime(String name) {
        return JBoltTimestampConverter.doConvertDateTime(get(name));
    }

    /**
     * 判断是否是Ajax请求
     *
     * @return
     */
    @NotAction
    public boolean isAjax() {
        return JBoltControllerKit.isAjax(this);
    }

    /**
     * 返回经过格式包装的JSON数据 带extraData
     *
     * @param data
     * @param extraData
     */
    @NotAction
    public void renderJBoltApiSuccessWithData(Object data, Object extraData) {
        renderJBoltApiSuccessWithData(data, extraData, false);
    }

    /**
     * 返回经过格式包装的JSON数据 带extraData
     *
     * @param data
     * @param extraData
     * @param useJfinalJson
     */
    @NotAction
    public void renderJBoltApiSuccessWithData(Object data, Object extraData, boolean useJfinalJson) {
        JBoltControllerKit.renderJBoltApiRet(this, JBoltApiRet.successWithData(data).set("extraData", extraData), useJfinalJson);
    }

    /**
     * 返回经过格式包装的JSON数据
     *
     * @param data
     */
    @NotAction
    public void renderJsonData(Object data) {
        JBoltControllerKit.renderJsonData(this, data);
    }

    /**
     * 返回经过格式包装的JSON数据
     *
     * @param data
     * @param useJfinalJson 是否使用JFinalJson序列化
     */
    @NotAction
    public void renderJsonData(Object data, boolean useJfinalJson) {
        JBoltControllerKit.renderJsonData(this, data, useJfinalJson);
    }

    /**
     * 返回经过格式包装的JSON数据
     *
     * @param data
     * @param msg
     */
    @NotAction
    public void renderJsonData(Object data, String msg) {
        JBoltControllerKit.renderJsonSuccess(this, msg, data);
    }

    /**
     * 返回经过包装的JSON成功信息
     */
    @NotAction
    public void renderJsonSuccess() {
        JBoltControllerKit.renderJsonSuccess(this);
    }

    /**
     * 返回经过包装的JSON成功信息
     *
     * @param msg
     */
    @NotAction
    public void renderJsonSuccess(String msg) {
        JBoltControllerKit.renderJsonSuccess(this, msg);
    }

    /**
     * 设置返回的错误信息内容
     */
    protected Ret setFailMsg(String msg) {
        return Ret.fail(msg);
    }

    /**
     * 返回经过格式包装的错误信息
     *
     * @param msg
     */
    @NotAction
    public void renderJsonFail(String msg) {
        JBoltControllerKit.renderJsonFail(this, msg);
    }

    /**
     * 设置返回信息内容
     *
     * @param msg
     */
    protected void setMsg(String msg) {
        set("msg", msg);
    }

    /**
     * 获取type参数
     *
     * @return
     */
    protected Integer getType() {
        return getInt("type");
    }

    /**
     * 获取参数值 BigDecimal类型
     *
     * @param paraName
     * @return
     */
    protected BigDecimal getBigDecimal(String paraName) {
        String value = get(paraName);
        if (value == null || value.length() == 0) {
            value = getAttr(paraName);
        }
        return value == null ? null : (value.length() == 0 ? null : new BigDecimal(value));
    }

    /**
     * 获取参数值 BigDecimal类型
     *
     * @param paraName
     * @return
     */
    protected BigDecimal getParaToBigDecimal(String paraName) {
        String value = get(paraName);
        return value == null ? null : (value.length() == 0 ? null : new BigDecimal(value));
    }

    /**
     * 获取参数值 BigDecimal类型
     *
     * @param attrName
     * @return
     */
    protected BigDecimal getAttrToBigDecimal(String attrName) {
        String value = getAttr(attrName);
        return value == null ? null : (value.length() == 0 ? null : new BigDecimal(value));
    }

    /**
     * 设置type属性
     *
     * @return
     */
    protected void setType(Integer type) {
        set("type", type);
    }

    /**
     * 获取key参数
     *
     * @return
     */
    protected String getKey() {
        return get("key");
    }

    /**
     * 常用关键词搜索 获得关键词参数
     *
     * @return
     */
    protected String getKeywords() {
        return get("keywords");
    }

    /**
     * 获取OfModule参数
     *
     * @return
     */
    protected Integer getOfModule() {
        return getInt("ofModule");
    }

    /**
     * 得到排序参数名
     *
     * @return
     */
    protected String getSortColumn() {
        return get("sortColumn");
    }

    /**
     * 得到排序参数名
     *
     * @param defaultValue
     * @return
     */
    protected String getSortColumn(String defaultValue) {
        if (isOk(defaultValue)) {
            set("sortColumn", defaultValue);
        }
        return get("sortColumn", defaultValue);
    }

    /**
     * 得到排序方式
     *
     * @return
     */
    protected String getSortType() {
        return get("sortType");
    }

    /**
     * 得到排序方式
     *
     * @param defaultValue
     * @return
     */
    protected String getSortType(String defaultValue) {
        if (isOk(defaultValue)) {
            set("sortType", defaultValue);
        }
        return get("sortType", defaultValue);
    }

    /**
     * 设置排序列
     *
     * @param sortColumn
     */
    protected void setSortColumn(String sortColumn) {
        set("sortColumn", sortColumn);
    }

    /**
     * 设置排序类型
     *
     * @param sortType
     */
    protected void setSortType(String sortType) {
        set("sortType", sortType);
    }

    /**
     * 设置页面jboltTable中默认显示按照哪列排序
     *
     * @param sortColumn
     * @param sortType
     */
    protected void setDefaultSortInfo(String sortColumn, String sortType) {
        setSortColumn(sortColumn);
        setSortType(sortType);
    }

    /**
     * 设置页面jboltTable中默认显示按照哪列排序
     *
     * @param sortInfo
     */
    protected void setDefaultSortInfo(SortInfo sortInfo) {
        set(sortInfo.getKey(), sortInfo);
    }

    /**
     * 设置页面jboltTable中默认显示按照哪列排序多个
     *
     * @param sortInfos
     */
    protected void setDefaultSortInfos(SortInfo... sortInfos) {
        if (isOk(sortInfos)) {
            for (SortInfo sortInfo : sortInfos) {
                set(sortInfo.getKey(), sortInfo);
            }
        }
    }

    /**
     * 设置页面jboltTable中默认显示按照哪列排序 多个
     *
     * @param sortInfos
     */
    protected void setDefaultSortInfos(List<SortInfo> sortInfos) {
        if (isOk(sortInfos)) {
            for (SortInfo sortInfo : sortInfos) {
                set(sortInfo.getKey(), sortInfo);
            }
        }
    }

    /**
     * 常用关键词搜索 获得关键词参数
     *
     * @return
     */
    protected void setKeywords(String keywords) {
        if (keywords != null && keywords.length() > 0) {
            set("keywords", keywords.replace("\"", "&quot;"));
        }
    }

    /**
     * 常用关键词搜索 获得关键词参数
     *
     * @return
     */
    protected String getKeywords(String key) {
        return get(key);
    }

    /**
     * 获得enable
     *
     * @return
     */
    protected Boolean getEnable() {
        return getBoolean("enable");
    }

    /**
     * 常用获得state状态参数
     *
     * @return
     */
    protected Integer getState() {
        return getInt("state");
    }

    /**
     * 常用获得state状态参数
     *
     * @return
     */
    protected Integer getState(Integer defaultValue) {
        return getInt("state", defaultValue);
    }

    /**
     * 从参数中获得分页 当前要查询第几页的pageNumber
     *
     * @return
     */
    protected Integer getPageNumber() {
        return getInt("page", 1);
    }

    /**
     * 从参数中获得分页 的每页数据量pageSize 默认10
     *
     * @return
     */
    protected Integer getPageSize() {
        return getInt("pageSize", JBoltPageSize.PAGESIZE_ADMIN_LIST);
    }

    /**
     * 从参数中获得分页 的每页数据量pageSize 自定义默认值
     *
     * @param defaultPageSize
     * @return
     */
    protected Integer getPageSize(int defaultPageSize) {
        return getInt("pageSize", defaultPageSize);
    }

    /**
     * 获取开始时间
     *
     * @return
     */
    protected Date getStartTime() {
        return getDate("startTime");
    }

    /**
     * 获取结束时间
     *
     * @return
     */
    protected Date getEndTime() {
        return getDate("endTime");
    }

    /**
     * 获取开始时间
     *
     * @param defaultValue
     * @return
     */
    protected Date getStartTime(Date defaultValue) {
        return getDate("startTime", defaultValue);
    }

    /**
     * 获取结束时间
     *
     * @param defaultValue
     * @return
     */
    protected Date getEndTime(Date defaultValue) {
        return getDate("endTime", defaultValue);
    }

    /**
     * 判断请求是否是Pjax请求
     *
     * @return
     */
    @NotAction
    public boolean isPjax() {
        return JBoltControllerKit.isPjax(this);
    }

    /**
     * 判断请求是否是ajaxPortal请求
     *
     * @return
     */
    @NotAction
    public boolean isAjaxPortal() {
        return JBoltControllerKit.isAjaxPortal(this);
    }

    /**
     * 获取Json数据
     *
     * @return
     */
    @NotAction
    public JSONObject getJSONObject() {
        return JBoltControllerKit.getJSONObject(this);
    }

    /**
     * 获取Json数据转为JSonArray
     *
     * @return
     */
    @NotAction
    public JSONArray getJSONArray() {
        return JBoltControllerKit.getJSONArray(this);
    }

    /**
     * 获取Json数据转为JSonArray
     *
     * @return
     */
    @NotAction
    public List<JSONObject> getJSONObjectList() {
        return JBoltControllerKit.getJSONObjectList(this);
    }

    /**
     * 获取Json数据 转为Java List
     *
     * @return
     */
    @NotAction
    public <T> List<T> getJSONList(Class<T> clazz) {
        return JBoltControllerKit.getJSONList(this, clazz);
    }

    /**
     * 根据key获取json数据 转为JSONObject
     *
     * @param key
     * @return
     */
    @NotAction
    public JSONObject getJSONObject(String key) {
        return JBoltControllerKit.getJSONObject(this, key);
    }

    /**
     * 根据key获取json数据 转为JSONArray
     *
     * @param key
     * @return
     */
    @NotAction
    public JSONArray getJSONArray(String key) {
        return JBoltControllerKit.getJSONArray(this, key);
    }

    /**
     * 根据key获取json数据 转为List<JSONObject>
     *
     * @param key
     * @return
     */
    @NotAction
    public List<JSONObject> getJSONObjectList(String key) {
        return JBoltControllerKit.getJSONObjectList(this, key);
    }

    /**
     * 根据key获取json数据 转为java List
     *
     * @param key
     * @return
     */
    @NotAction
    public <T> List<T> getJSONList(String key, Class<T> clazz) {
        return JBoltControllerKit.getJSONList(this, key, clazz);
    }

    /**
     * JBolt定制getModel实现
     */
    @Override
    @NotAction
    public <T> T getModel(Class<T> modelClass) {
        return JBoltInjector.injectModel(modelClass, getRequest(), false);
    }

    @Override
    @NotAction
    public <T> T getModel(Class<T> modelClass, boolean skipConvertError) {
        return JBoltInjector.injectModel(modelClass, getRequest(), skipConvertError);
    }

    /**
     * Get model from http request.
     */
    @Override
    @NotAction
    public <T> T getModel(Class<T> modelClass, String modelName) {
        return JBoltInjector.injectModel(modelClass, modelName, getRequest(), false);
    }

    @Override
    @NotAction
    public <T> T getModel(Class<T> modelClass, String modelName, boolean skipConvertError) {
        return JBoltInjector.injectModel(modelClass, modelName, getRequest(), skipConvertError);
    }

    @Override
    @NotAction
    public <T> T getBean(Class<T> beanClass) {
        return JBoltInjector.injectBean(beanClass, getRequest(), false);
    }

    @Override
    @NotAction
    public <T> T getBean(Class<T> beanClass, boolean skipConvertError) {
        return JBoltInjector.injectBean(beanClass, getRequest(), skipConvertError);
    }

    @Override
    @NotAction
    public <T> T getBean(Class<T> beanClass, String beanName) {
        return JBoltInjector.injectBean(beanClass, beanName, getRequest(), false);
    }

    @Override
    @NotAction
    public <T> T getBean(Class<T> beanClass, String beanName, boolean skipConvertError) {
        return JBoltInjector.injectBean(beanClass, beanName, getRequest(), skipConvertError);
    }

    /**
     * 根据类型生成验证码
     *
     * @param type
     */
    @NotAction
    public void renderJBoltCaptcha(String type) {
        render(new JBoltCaptchaRender(type));
    }

    /**
     * 验证码校验
     *
     * @param captchaParaName
     * @return
     */
    @NotAction
    @Override
    public boolean validateCaptcha(String captchaParaName) {
        String captchaInput = get(captchaParaName);
        if (notOk(captchaInput)) {
            return false;
        }
        String jbcapkey = StrKit.defaultIfBlank(get(JBoltCaptchaRender.CAPTCHA_KEY), getHeader(JBoltCaptchaRender.CAPTCHA_KEY));
        if (notOk(jbcapkey)) {
            return false;
        }
        return com.jfinal.captcha.CaptchaRender.validate(jbcapkey, captchaInput);
    }

    /**
     * 返回气泡PNG 验证码
     */
    @NotAction
    public void renderBubblePngCaptcha() {
        render(new JBoltCaptchaRender(JBoltConst.JBOLT_CAPTCHA_TYPE_BUBBLE_PNG));
    }

    /**
     * 返回gif格式的验证码
     */
    @NotAction
    public void renderGifCaptcha() {
        render(new JBoltCaptchaRender(JBoltConst.JBOLT_CAPTCHA_TYPE_GIF));
    }

    /**
     * 获取Checkbox选中值
     *
     * @param name
     * @return
     */
    @NotAction
    public String[] getCheckBoxValues(String name) {
        return getParaValues(name);
    }

    /**
     * 获取Checkbox是否选中 仅用于一个的时候
     *
     * @param name
     * @return
     */
    @NotAction
    public boolean getCheckBoxBoolean(String name) {
        return JBoltConst.CHECKBOX_ON.equalsIgnoreCase(getPara(name));
    }

    /**
     * 获取Checkbox选中值
     *
     * @param name
     * @return
     */
    @NotAction
    public Integer[] getCheckBoxValuesToInt(String name) {
        return getParaValuesToInt(name);
    }

    /**
     * 获取Checkbox选中值
     *
     * @param name
     * @return
     */
    @NotAction
    public Long[] getCheckBoxValuesToLong(String name) {
        return getParaValuesToLong(name);
    }

    /**
     * 保持住表单checkbox的值
     *
     * @param name
     */
    @NotAction
    public void keepCheckbox(String name) {
        String[] checkValues = getCheckBoxValues(name);
        if (checkValues != null && checkValues.length > 0) {
            set(name, JBoltArrayUtil.join(checkValues, ","));
        }
    }

    @NotAction
    @Override
    public Controller keepModel(Class<? extends Model> modelClass, String modelName) {
        if (StrKit.notBlank(modelName)) {
            set(modelName, getModel(modelClass, modelName, true));
        } else {
            keepPara();
        }
        return this;
    }

    @NotAction
    @Override
    public Controller keepBean(Class<?> beanClass, String beanName) {
        if (StrKit.notBlank(beanName)) {
            set(beanName, getBean(beanClass, beanName, true));
        } else {
            keepPara();
        }
        return this;
    }

    /**
     * 批量获取数据转为Model 模型驱动 需要符合格式 modelName[0...n].attrName格式的 例如
     * <input type="text" name="user[0].name" />
     * <input type="text" name="user[1].name" />
     *
     * @param <T>
     * @param modelClass
     * @param modelName
     * @return
     */
    protected <T> List<T> getModels(Class<T> modelClass, String modelName) {
        // 正则匹配modelName[0...n]
        Pattern p = Pattern.compile(modelName + "\\[\\d+\\].[a-zA-z0-9]+");
        // 拿到参数map
        Map<String, String[]> paraMap = getParaMap();
        // 构建不重复modelNameSet
        String paraKey;
        Set<String> modelNameSet = new HashSet<String>();
        for (Entry<String, String[]> e : paraMap.entrySet()) {
            paraKey = e.getKey();
            if (p.matcher(paraKey).find()) {
                modelNameSet.add(paraKey.split("\\.")[0]);
            }
        }

        // 遍历set 获取对应Model 加到List中
        List<T> list = new ArrayList<T>();
        for (String mdName : modelNameSet) {
            list.add(getModel(modelClass, mdName));
        }
        return list;
    }

    /**
     * 批量获取数据转为javaBean 必须符合beanName[0...n].attrName格式的
     *
     * @param <T>
     * @param beanClass
     * @param beanName
     * @return
     */
    protected <T> List<T> getBeans(Class<T> beanClass, String beanName) {
        Pattern p = Pattern.compile(beanName + "\\[\\d+\\].[a-zA-z0-9]+");
        Map<String, String[]> paraMap = getParaMap();
        String paraKey;
        Set<String> beanNameSet = new HashSet<String>();
        for (Entry<String, String[]> e : paraMap.entrySet()) {
            paraKey = e.getKey();
            if (p.matcher(paraKey).find()) {
                beanNameSet.add(paraKey.split("\\.")[0]);
            }
        }
        List<T> list = new ArrayList<T>();
        for (String bName : beanNameSet) {
            list.add(getBean(beanClass, bName));
        }
        return list;
    }

    /**
     * 渲染字符串byte 默认PDF 在线查看
     *
     * @param data
     */
    protected void renderByteToPDF(String data) {
        render(JBoltByteRender.create(data.toString().getBytes()));
    }

    /**
     * 渲染bytes 默认PDF 在线查看
     *
     * @param bytes
     */
    protected void renderByteToPDF(byte[] bytes) {
        render(JBoltByteRender.create(bytes));
    }

    /**
     * 渲染bytes 为普通文本数据流 在线查看
     *
     * @param bytes
     */
    protected void renderByteToTXT(byte[] bytes) {
        render(JBoltByteRender.create(bytes).setFileType(JBoltByteFileType.TXT));
    }

    /**
     * 渲染字符串 为普通文本数据流 在线查看
     *
     * @param str
     */
    protected void renderStringToTXT(String str) {
        renderByteToTXT(StrUtil.utf8Bytes(str));
    }

    /**
     * 渲染bytes 为图片数据流 在线查看
     *
     * @param bytes
     */
    protected void renderByteToImage(byte[] bytes) {
        render(JBoltByteRender.create(bytes).setFileType(JBoltByteFileType.JPG));
    }

    /**
     * 渲染bytes 为图片数据流 下载文件
     *
     * @param bytes
     * @param fileName
     */
    protected void renderByteToImageFile(byte[] bytes, String fileName) {
        render(JBoltByteRender.create(bytes).setFileType(JBoltByteFileType.JPG).setFileName(fileName)
                .setRenderType(JBoltByteRenderType.DOWNLOAD));
    }

    /**
     * 渲染bytes 为PDF数据流 下载文件
     *
     * @param bytes
     * @param fileName
     */
    protected void renderByteToPDFFile(byte[] bytes, String fileName) {
        render(JBoltByteRender.create(bytes).setFileType(JBoltByteFileType.PDF).setFileName(fileName)
                .setRenderType(JBoltByteRenderType.DOWNLOAD));
    }

    /**
     * 渲染字符串 为PDF数据流 下载文件
     *
     * @param str
     * @param fileName
     */
    protected void renderStringToPDFFile(String str, String fileName) {
        renderByteToPDFFile(StrUtil.utf8Bytes(str), fileName);
    }

    /**
     * 渲染bytes 为HTML数据流 下载文件
     *
     * @param bytes
     * @param fileName
     */
    @NotAction
    public void renderByteToHTMLFile(byte[] bytes, String fileName) {
        render(JBoltByteRender.create(bytes).setFileType(JBoltByteFileType.HTML).setFileName(fileName)
                .setRenderType(JBoltByteRenderType.DOWNLOAD));
    }

    /**
     * 渲染字符串 为Html数据流 下载文件
     *
     * @param str
     * @param fileName
     */
    @NotAction
    public void renderStringToHTMLFile(String str, String fileName) {
        renderByteToHTMLFile(StrUtil.utf8Bytes(str), fileName);
    }


    /**
     * 渲染bytes 为TXT数据流 下载文件
     *
     * @param bytes
     * @param fileName
     */
    protected void renderByteToTXTFile(byte[] bytes, String fileName) {
        render(JBoltByteRender.create(bytes).setFileType(JBoltByteFileType.TXT).setFileName(fileName)
                .setRenderType(JBoltByteRenderType.DOWNLOAD));
    }

    /**
     * 渲染字符串 为TXT数据流 下载文件
     *
     * @param str
     * @param fileName
     */
    protected void renderStringToTXTFile(String str, String fileName) {
        renderByteToTXTFile(StrUtil.utf8Bytes(str), fileName);
    }

    /**
     * 渲染bytes 为excel数据流 下载文件 xls 2003
     *
     * @param bytes
     * @param fileName
     */
    protected void renderBytesToExcelXlsFile(byte[] bytes, String fileName) {
        render(JBoltByteRender.create(bytes).setFileType(JBoltByteFileType.XLS).setFileName(fileName)
                .setRenderType(JBoltByteRenderType.DOWNLOAD));
    }

    /**
     * 渲染JBoltExcel 下载文件 xls 2003
     *
     * @param excel
     */
    protected void renderBytesToExcelXlsFile(JBoltExcel excel) {
        renderBytesToExcelXlsFile(excel.setXlsx(false).toByteArray(), excel.getFileName());
    }

    /**
     * 渲染bytes 为excel数据流 下载文件 xlsx 2007
     *
     * @param bytes
     * @param fileName
     */
    protected void renderBytesToExcelXlsxFile(byte[] bytes, String fileName) {
        render(JBoltByteRender.create(bytes).setFileType(JBoltByteFileType.XLSX).setFileName(fileName)
                .setRenderType(JBoltByteRenderType.DOWNLOAD));
    }

    /**
     * 渲染JBoltExcel下载文件 xlsx 2007
     *
     * @param excel
     */
    protected void renderBytesToExcelXlsxFile(JBoltExcel excel) {
        renderBytesToExcelXlsxFile(excel.setXlsx(true).toByteArray(), excel.getFileName());
    }

    /**
     * 渲染bytes 为word数据流 下载文件 docx 2007
     *
     * @param bytes
     * @param fileName
     */
    protected void renderBytesToWordDocxFile(byte[] bytes, String fileName) {
        render(JBoltByteRender.create(bytes).setFileType(JBoltByteFileType.DOCX).setFileName(fileName)
                .setRenderType(JBoltByteRenderType.DOWNLOAD));
    }

    /**
     * 渲染JBoltWord下载文件 docx 2007
     *
     * @param word
     */
    protected void renderBytesToWordDocxFile(JBoltWord word) {
        renderBytesToWordDocxFile(word.toByteArray(), word.getFileName());
    }

    /**
     * 渲染bytes 为word数据流 下载文件 doc 2003
     *
     * @param bytes
     * @param fileName
     */
    protected void renderBytesToWordDocFile(byte[] bytes, String fileName) {
        render(JBoltByteRender.create(bytes).setFileType(JBoltByteFileType.DOC).setFileName(fileName)
                .setRenderType(JBoltByteRenderType.DOWNLOAD));
    }

    /**
     * 渲染JBoltWord下载文件 doc 2003
     *
     * @param word
     */
    protected void renderBytesToWordDocFile(JBoltWord word) {
        renderBytesToWordDocFile(word.toByteArray(), word.getFileName());
    }

    /**
     * 从请求里使用万能参数JBoltPara接收
     *
     * @return
     */
    protected JBoltPara getJBoltPara() {
        return JBoltControllerKit.getJBoltPara(this);
    }

    /**
     * 得到参数转数组
     *
     * @param paraName
     * @return
     */
    protected String[] getStrParaToArray(String paraName) {
        return getStrParaToArray(paraName, ",");
    }

    /**
     * 得到参数转数组
     *
     * @param paraName
     * @return
     */
    protected String[] getStrParaToArray(String paraName, String split) {
        String para = getPara(paraName);
        if (notOk(para)) {
            return null;
        }
        return JBoltArrayUtil.from(para, split);
    }

    /**
     * 判断不是excel文件
     */
    @Override
    public boolean notExcel(UploadFile file) {
        return JBoltParaValidator.notExcel(file);
    }

    /**
     * 判断是否excel文件
     */
    @Override
    public boolean isExcel(UploadFile file) {
        return JBoltParaValidator.isExcel(file);
    }

    /**
     * 获取typeId参数的值 Long类型 默认从url参数index=0找
     * 找不到就找name=typeId的参数
     *
     * @return
     */
    protected Long getTypeId() {
        Long typeId = getLong(0);
        if (notOk(typeId)) {
            typeId = getLong(TYPE_ID_PARAM_NAME);
        }
        return typeId;
    }

    /**
     * 获取typeId的值 int类型 默认从url参数index=0找
     * 找不到就找name=typeId的参数
     *
     * @return
     */
    protected Integer getTypeIdToInt() {
        Integer typeId = getInt(0);
        if (notOk(typeId)) {
            typeId = getInt(TYPE_ID_PARAM_NAME);
        }
        return typeId;
    }

    /**
     * 获取ID主键的值 默认从url参数index=0找
     * 找不到就找name=id的参数
     *
     * @return
     */
    protected <T> T getId() {
        Class<? extends JBoltBaseModel> clazz = getModelService().getModelClass();
        Object id = getId(clazz);
        if (notOk(id)) {
            id = getId(ID_PARAM_NAME,clazz);
        }
        return (T) id;
    }

    /**
     * 获取ids参数
     *
     * @return
     */
    protected String getIds() {
        return get("ids");
    }

    /**
     * 获取ids参数
     *
     * @return
     */
    protected Long[] getIdsToLongArray() {
        return getParaToLongArray("ids");
    }

    /**
     * 获取ID主键的值 默认从url参数index=0找
     * 找不到就找name=id的参数
     *
     * @return
     */
    protected Integer getIdToInt() {
        Integer id = getInt(0);
        if (notOk(id)) {
            id = getInt(ID_PARAM_NAME);
        }
        return id;
    }

    /**
     * 获取ID主键的值 默认从url参数index=0找
     * 找不到就找name=id的参数
     *
     * @return
     */
    protected Long getIdToLong() {
        Long id = getLong(0);
        if (notOk(id)) {
            id = getLong(ID_PARAM_NAME);
        }
        return id;
    }

    /**
     * 获取ID主键的值 默认从url参数index=0找
     * 找不到就找name=id的参数
     *
     * @return
     */
    protected String getIdToStr() {
        String id = get(0);
        if (notOk(id)) {
            id = get(ID_PARAM_NAME);
        }
        return id;
    }


    /**
     * 获取PID父节点主键的值 默认从url参数index=0找
     * 找不到就找name=pid的参数
     *
     * @return
     */
    protected Long getPid() {
        Long pid = getLong(0);
        if (notOk(pid)) {
            pid = getLong(PID_PARAM_NAME);
        }
        return pid;
    }

    /**
     * 获取PID父节点主键的值 默认从url参数index=0找
     * 找不到就找name=pid的参数
     *
     * @return
     */
    protected Integer getPidToInt() {
        Integer pid = getInt(0);
        if (notOk(pid)) {
            pid = getInt(PID_PARAM_NAME);
        }
        return pid;
    }

    /**
     * 获取ID主键的值 默认从url参数index=0找
     * 找不到就找name=pid的参数
     *
     * @return
     */
    protected String getPidToStr() {
        String pid = get(0);
        if (notOk(pid)) {
            pid = get(PID_PARAM_NAME);
        }
        return pid;
    }

    /**
     * 获取主键类数据 从请求里获取对应model类下数据库的主键ID值
     *
     * @param modelClass
     * @return
     */
    protected Object getId(Class<? extends Model> modelClass) {
        return getId(0, modelClass);
    }

    /**
     * 获取主键类数据 从请求里获取对应model类下数据库的主键ID值
     *
     * @param paraName
     * @param modelClass
     * @return
     */
    protected Object getId(String paraName, Class<? extends Model> modelClass) {
        return JBoltControllerKit.getId(this, paraName, modelClass);
    }

    /**
     * 获取主键类数据
     *
     * @param index
     * @param modelClass
     * @return
     */
    protected Object getId(int index, Class<? extends Model> modelClass) {
        return JBoltControllerKit.getId(this, index, modelClass);
    }

    /**
     * 获取JBoltTable组件提交数据
     *
     * @return
     */
    protected JBoltTable getJBoltTable() {
        return JBoltControllerKit.getJBoltTable(this);
    }

    /**
     * 获取多个JBoltTable组件提交数据
     *
     * @return
     */
    protected JBoltTableMulti getJBoltTables() {
        return JBoltControllerKit.getJBoltTables(this);
    }

    /**
     * 设置dateRange变量 客户端可以用
     *
     * @param dateRange
     */
    protected void setDateRange(String dateRange) {
        JBoltControllerKit.setDateRange(this, dateRange);
    }

    /**
     * 设置dateRange变量 客户端可以用
     *
     * @param dateRange
     */
    protected void setDateRange(JBoltDateRange dateRange) {
        JBoltControllerKit.setDateRange(this, dateRange);
    }

    /**
     * 设置dateRange变量 客户端可以用
     *
     * @param dateRange
     * @param defaultDataRangeStr
     */
    protected void setDateRange(JBoltDateRange dateRange, String defaultDataRangeStr) {
        JBoltControllerKit.setDateRange(this, dateRange, defaultDataRangeStr);
    }

    /**
     * 设置dateRange变量 客户端可以用
     *
     * @param attrName
     * @param dateRange
     */
    protected void setDateRange(String attrName, String dateRange) {
        JBoltControllerKit.setDateRange(this, attrName, dateRange);
    }

    /**
     * 获取dateRange组件数据
     *
     * @return
     */
    protected JBoltDateRange getDateRange() {
        return JBoltControllerKit.getDateRange(this);
    }

    /**
     * 获取dateRange组件数据
     *
     * @param type
     * @return
     */
    protected JBoltDateRange getDateRange(String type) {
        return JBoltControllerKit.getDateRange(this, type);
    }

    /**
     * 获取dateRange组件数据
     *
     * @param paraName
     * @param type
     * @return
     */
    protected JBoltDateRange getDateRange(String paraName, String type) {
        return JBoltControllerKit.getDateRange(this, paraName, type);
    }

    /**
     * 得到参数字符串 分割为数组
     *
     * @param paraName
     */
    protected String[] getParaToArray(String paraName) {
        return JBoltControllerKit.getParaToArray(this, paraName);
    }

    /**
     * 得到参数字符串 分割为数组
     *
     * @param paraName
     * @param split
     */
    protected String[] getParaToArray(String paraName, String split) {
        return JBoltControllerKit.getParaToArray(this, paraName, split);
    }

    /**
     * 得到参数字符串 分割为int数组
     *
     * @param paraName
     * @param split
     */
    protected Integer[] getParaToIntArray(String paraName, String split) {
        return JBoltControllerKit.getParaToIntArray(this, paraName, split);
    }

    /**
     * 得到参数字符串 分割为int数组
     *
     * @param paraName
     */
    protected Integer[] getParaToIntArray(String paraName) {
        return JBoltControllerKit.getParaToIntArray(this, paraName);
    }

    /**
     * 得到参数字符串 分割为long数组
     *
     * @param paraName
     * @param split
     */
    protected Long[] getParaToLongArray(String paraName, String split) {
        return JBoltControllerKit.getParaToLongArray(this, paraName, split);
    }

    /**
     * 得到参数字符串 分割为long数组
     *
     * @param paraName
     */
    protected Long[] getParaToLongArray(String paraName) {
        return JBoltControllerKit.getParaToLongArray(this, paraName);
    }


    protected RangeSlider getRangeSlider(int index) {
        return JBoltControllerKit.getRangeSlider(this, index);
    }

    protected RangeSlider getRangeSlider(int index, String split) {
        return JBoltControllerKit.getRangeSlider(this, index, split);
    }


    protected RangeSlider getRangeSlider(String paraName) {
        return JBoltControllerKit.getRangeSlider(this, paraName);
    }

    protected RangeSlider getRangeSlider(String paraName, String split) {
        return JBoltControllerKit.getRangeSlider(this, paraName, split);
    }

    protected JBoltApiRet findModelById(String... returnColumns) {
        return findModelById(getId(), returnColumns);
    }

    protected JBoltApiRet findModelById(Object id, String... returnColumns) {
        if (notOk(id)) {
            return JBoltApiRet.API_FAIL(JBoltMsg.PARAM_ERROR);
        }
        Object o = isOk(returnColumns) ? getModelService().findByIdLoadColumns(id, returnColumns) : getModelService().findById(id);
        if (o == null) {
            return JBoltApiRet.API_FAIL(JBoltMsg.DATA_NOT_EXIST);
        }
        return JBoltApiRet.successWithData(o);
    }

    protected JBoltApiRet findRecordById(boolean columnToCamelCase, String... returnColumns) {
        return findRecordById(getId(), columnToCamelCase, returnColumns);
    }

    protected JBoltApiRet findRecordById(Object id, boolean columnToCamelCase, String... returnColumns) {
        if (notOk(id)) {
            return JBoltApiRet.API_FAIL(JBoltMsg.PARAM_ERROR);
        }
        Object o = isOk(returnColumns) ? getModelService().findRecordById(id, columnToCamelCase, returnColumns) : getModelService().findRecordById(id, columnToCamelCase);
        if (o == null) {
            return JBoltApiRet.API_FAIL(JBoltMsg.DATA_NOT_EXIST);
        }
        return JBoltApiRet.successWithData(o);
    }

    /**
     * 切换enable
     * @return
     */
    protected Ret toggleModelEnableById(){
        return getModelService().toggleEnable(getId());
    }
    /**
     * 根据ID切换Boolean类型column
     * @param columns
     * @return
     */
    protected Ret toggleModelBooleanColumnById(String... columns){
        return getModelService().toggleBoolean(getId());
    }
    /**
     * 根据ID切换Boolean类型column
     * @param extraData
     * @param columns
     * @return
     */
    protected Ret toggleModelBooleanColumnById(Kv extraData,String... columns){
        return getModelService().toggleBoolean(extraData,getId(),columns);
    }
    /**
     * 根据ID删除
     */
    protected Ret deleteModelById(){
        return deleteModelById(false);
    }
    /**
     * 根据ID删除 指定检测是否可以删除
     * @param checkCanDelete
     */
    protected Ret deleteModelById(boolean checkCanDelete){
        return getModelService().deleteById(getId(),checkCanDelete);
    }

    /**
     * 根据ID删除 realDelete
     */
    protected Ret realDeleteModelById(){
        return realDeleteModelById(false);
    }
    /**
     * 根据ID删除 指定检测是否可以删除 realDelete
     * @param checkCanDelete
     */
    protected Ret realDeleteModelById(boolean checkCanDelete){
        return getModelService().realDeleteById(getId(),checkCanDelete);
    }

    /**
     * 根据IDs 批量删除 realDelete
     */
    protected Ret realDeleteModelByIds(){
        return realDeleteModelByIds(false);
    }
    /**
     * 根据IDs 批量删除 指定检测是否可以删除 realDelete
     * @param checkCanDelete
     */
    protected Ret realDeleteModelByIds(boolean checkCanDelete){
        return getModelService().realDeleteByIds(getIds(),checkCanDelete);
    }
    /**
     * 根据ID恢复假删除数据
     */
    protected Ret recoverModelById(){
        return recoverModelById(false);
    }
    /**
     * 根据ID恢复假删除数据
     * @param checkCanRecover
     */
    protected Ret recoverModelById(boolean checkCanRecover){
        return getModelService().recoverById(getId(),checkCanRecover);
    }

    /**
     * 根据IDs 批量恢复假删除数据
     */
    protected Ret recoverModelByIds(){
        return recoverModelByIds(false);
    }
    /**
     * 根据IDs 批量恢复假删除数据
     * @param checkCanRecover
     */
    protected Ret recoverModelByIds(boolean checkCanRecover){
        return getModelService().recoverByIds(getIds(),checkCanRecover);
    }

    /**
     * 根据IDs批量删除
     */
    protected Ret deleteModelByIds(){
        return deleteModelByIds(false);
    }
    /**
     * 根据IDs批量删除
     * @param checkCanDelete
     */
    protected Ret deleteModelByIds(boolean checkCanDelete){
        return getModelService().deleteByIds(getIds(),checkCanDelete);
    }

    protected abstract JBoltBaseService<? extends JBoltBaseModel<?>> getModelService();
    protected JBoltFormData getJBoltFormData(Consumer<UploadProgress> callback) throws Exception {
        return getJBoltFormData(null,callback);
    }
    protected JBoltFormData getJBoltFormData() throws Exception {
        return getJBoltFormData(null,null);
    }
    protected JBoltFormData getJBoltFormData(String uploadPath) throws Exception {
        return getJBoltFormData(uploadPath,null);
    }
    protected JBoltFormData getJBoltFormData(String uploadPath, Consumer<UploadProgress> callback) throws Exception {
        HttpServletRequest request = getRequest();
        // 检查请求是否包含文件上传
        if (!ServletFileUpload.isMultipartContent(request)) {
            return null;
        }
        JBoltFormData formData = new JBoltFormData();
        // 创建文件项工厂
        FileItemFactory factory = new DiskFileItemFactory();
        // 创建上传处理器
        ServletFileUpload upload = new ServletFileUpload(factory);
        if(callback!=null) {
            // 创建进度监听器
            ProgressListener progressListener = new ProgressListener() {
                @Override
                public void update(long bytesRead, long contentLength, int items) {
                    callback.accept(new UploadProgress(items, contentLength, bytesRead));
                }
            };
            // 将进度监听器添加到上传处理器
            upload.setProgressListener(progressListener);
        }
            // 解析上传的表单数据
            List<FileItem> formItems = upload.parseRequest(request);
            if (formItems != null && !formItems.isEmpty()) {
                if(notOk(uploadPath)){
                    FileItem pathItem = formItems.stream().filter(item->item.isFormField() && "path".equals(item.getFieldName())).findFirst().orElse(null);
                    if(pathItem != null){
                        uploadPath = pathItem.getString();
                        if (notOk(uploadPath)) {
                            throw new RuntimeException("path参数异常:不能为空");
                        }
                        if (FileUtil.isAbsolutePath(uploadPath) || uploadPath.startsWith(".") || uploadPath.startsWith("/") || uploadPath.contains(":") || uploadPath.contains("\\")) {
                            throw new RuntimeException("path参数异常 例:avatar/{date(yyMMddHH)}/{random(5)}/{ori}");
                        }
                        String fileSuffix = FileUtil.getSuffix(uploadPath);
                        if (notOk(fileSuffix)) {
                            throw new RuntimeException("path参数异常 末尾未指定文件名 例:avatar/{date(yyMMddHH)}/{random(5)}/{ori}");
                        }
                    }
                }
                for (FileItem item : formItems) {
                    // 处理普通表单字段
                    if (item.isFormField()) {
                        formData.set(item.getFieldName(), item.getString());
                    } else {
                        genUploadFile(formData,item,uploadPath,FileUtil.getName(uploadPath));
                    }
                }
            }

        return formData;
    }

    private void genUploadFile(JBoltFormData formData, FileItem item,String uploadPath,String assignFileName) throws Exception {
        boolean assign = isOk(assignFileName);
        if(assign){
            uploadPath = uploadPath.replace("/"+assignFileName,"");
        }
        String originFileName = item.getName();
        String finalUploadPath = JFinal.me().getConstants().getBaseUploadPath() + (StrKit.isBlank(uploadPath) ? "" : (File.separator + uploadPath));
        String newFileName = assign?newFileName(finalUploadPath,assignFileName):ProgressUploadFileConfig.getRenameFunc().call(finalUploadPath, originFileName);
        String filePath = finalUploadPath + File.separator + newFileName;
        File storeFile = new File(filePath);
        // 保存文件到硬盘
        item.write(storeFile);
        formData.addUploadFile(new UploadFile(item.getFieldName(), finalUploadPath, storeFile.getName(), originFileName, item.getContentType()));
    }

    private String newFileName(String directory, String originFileName) {
        File file = new File(FileUtil.normalize(directory + File.separator + originFileName));
        int count = 1;
        String newFilename = originFileName;
        while (file.exists()) {
            int dotIndex = originFileName.lastIndexOf(".");
            String extension = "";
            if (dotIndex != -1) {
                extension = originFileName.substring(dotIndex);
                newFilename = originFileName.substring(0, dotIndex) + "_" + count + extension;
            } else {
                newFilename = originFileName + "_" + count;
            }
            file = new File(FileUtil.normalize(directory +File.separator+ newFilename));
            count++;
        }
        return newFilename;
    }
}
